#include "leos_ipc.h"
#include "leos_thread.h"

void le_ipc_thread_suspend(le_thread_t  thread,
                           le_list_t   *list)
{
    le_thread_suspend(thread);
    le_list_insert_before(&thread->tlist, list);
}

void le_ipc_thread_resume(le_list_t    *list)
{
    le_thread_t thread = le_container_of(list->next, struct le_thread, tlist);

    le_thread_resume(thread);
}

void le_sem_init(le_sem_t sem, le_int8_t value)
{
    le_list_init(&sem->suspend_thread);
    sem->value = value;
}

void le_sem_take(le_sem_t sem, le_int32_t tick)
{
    le_int32_t level = le_hw_interrupt_disable();

    le_thread_t thread = le_current_thread;

    if (sem->value > 0) {
        sem->value--;
        le_hw_interrupt_enable(level);
        return;
    }

    if (tick == 0) {
        le_hw_interrupt_enable(level);
        return;
    }

    le_ipc_thread_suspend(thread, &sem->suspend_thread);

    if (tick > 0) {
        le_timer_start(&thread->thread_timer, le_tick_get() + tick);
    }

    le_schedule();

    le_hw_interrupt_enable(level);
}

void le_sem_release(le_sem_t sem)
{
    le_int32_t level = le_hw_interrupt_disable();

    if (!le_list_isempty(&sem->suspend_thread)) {
        le_ipc_thread_resume(&sem->suspend_thread);
        le_schedule();
    } else {
        if (sem->value <= LE_SEM_VALUE_MAX) {
            sem->value++;
        }
    }

    le_hw_interrupt_enable(level);
}
